//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.quickserver.util.io;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.quickserver.net.server.ClientHandler;
import org.quickserver.net.server.impl.NonBlockingClientHandler;

public class ByteBufferOutputStream extends OutputStream {
    private static Logger logger = Logger.getLogger(ByteBufferOutputStream.class.getName());
    private ArrayList bufferList;
    private ByteBuffer lastByteBuffer = null;
    private NonBlockingClientHandler handler;
    private Object toNotify = null;
    private ArrayList encryptedBufferList;

    static {
        logger.setLevel(Level.INFO);
    }

    public static void setDebug(boolean flag) {
        if (flag) {
            logger.setLevel(Level.FINEST);
        } else {
            logger.setLevel(Level.INFO);
        }

    }

    public static boolean isLoggable(Level level) {
        return logger.isLoggable(level);
    }

    public ByteBufferOutputStream(ArrayList bufferList, ClientHandler handler) {
        if (bufferList != null && handler != null) {
            this.bufferList = bufferList;
            this.handler = (NonBlockingClientHandler)handler;
            if (handler.isSecure()) {
                this.encryptedBufferList = new ArrayList();
            }

        } else {
            throw new IllegalArgumentException("ArrayList or ClientHandler was null.");
        }
    }

    public synchronized void close() {
        if (this.lastByteBuffer != null) {
            this.returnBufferBack(this.lastByteBuffer);
        }

    }

    public void flush() throws IOException {
        if (this.bufferList.size() != 0 || this.lastByteBuffer != null) {
            this.handler.registerWrite();

            while(this.bufferList.size() >= 5) {
                this.handler.waitTillFullyWritten();
            }

        }
    }

    public synchronized void write(int b) throws IOException {
        this.handler.isConnected();
        ByteBuffer byteBuffer = null;
        if (this.bufferList.size() != 0) {
            byteBuffer = (ByteBuffer)this.bufferList.remove(this.bufferList.size() - 1);
            if (byteBuffer.remaining() == 0) {
                this.bufferList.add(byteBuffer);
                byteBuffer = null;
            }
        }

        try {
            if (byteBuffer == null) {
                byteBuffer = (ByteBuffer)this.handler.getServer().getByteBufferPool().borrowObject();
            }
        } catch (Exception var4) {
            logger.warning("Could not borrow ByteBufer from pool: " + var4);
            throw new IOException(var4.toString());
        }

        byteBuffer.put((byte)b);
        this.bufferList.add(byteBuffer);
    }

    public void write(byte[] b) throws IOException {
        this.write(b, 0, b.length);
    }

    public synchronized void write(byte[] b, int off, int len) throws IOException {
        if (len != 0) {
            this.handler.isConnected();
            ByteBuffer byteBuffer = null;
            int remaining = 0;
            int toWrite = len;
            if (len != 0 && this.bufferList.size() != 0) {
                byteBuffer = (ByteBuffer)this.bufferList.remove(this.bufferList.size() - 1);
                if (byteBuffer.remaining() == 0) {
                    this.bufferList.add(byteBuffer);
                    byteBuffer = null;
                }
            }

            while(toWrite != 0) {
                try {
                    if (byteBuffer == null) {
                        byteBuffer = (ByteBuffer)this.handler.getServer().getByteBufferPool().borrowObject();
                    }
                } catch (Exception var8) {
                    logger.warning("Could not borrow ByteBufer from pool: " + var8);
                    throw new IOException(var8.toString());
                }

                remaining = byteBuffer.remaining();
                if (remaining < toWrite) {
                    byteBuffer.put(b, off, remaining);
                    off += remaining;
                    toWrite -= remaining;
                } else {
                    byteBuffer.put(b, off, toWrite);
                    toWrite = 0;
                }

                this.bufferList.add(byteBuffer);
                byteBuffer = null;
            }

        }
    }

    public synchronized boolean writeAllByteBuffer() throws IOException {
        if (this.lastByteBuffer != null) {
            this.writeLastByteBuffer();
            if (this.lastByteBuffer != null) {
                return false;
            }
        }

        ByteBuffer dest = null;

        while(this.bufferList.size() != 0) {
            dest = (ByteBuffer)this.bufferList.remove(0);
            if (!this.handler.isSecure()) {
                this.lastByteBuffer = dest;
                this.lastByteBuffer.flip();
                this.writeLastByteBuffer();
                if (this.lastByteBuffer != null) {
                    return false;
                }
            } else {
                this.lastByteBuffer = this.handler.encrypt(dest);
                if (this.lastByteBuffer == null) {
                    this.bufferList.add(0, dest);
                    return false;
                }

                this.addEncryptedByteBuffer(this.lastByteBuffer);
                this.lastByteBuffer = null;
            }
        }

        while(this.encryptedBufferList != null && this.encryptedBufferList.size() != 0) {
            this.lastByteBuffer = (ByteBuffer)this.encryptedBufferList.remove(0);
            logger.fine("Sening to peer: " + this.lastByteBuffer.position());
            this.lastByteBuffer.flip();
            this.writeLastByteBuffer();
            if (this.lastByteBuffer != null) {
                return false;
            }
        }

        if (this.toNotify != null) {
            Object var2 = this.toNotify;
            synchronized(this.toNotify) {
                this.toNotify.notify();
                this.toNotify = null;
            }
        }

        logger.fine("writeAllByteBuffer is true!");
        return true;
    }

    private synchronized void writeLastByteBuffer() throws IOException {
        boolean var1 = false;

        while(this.lastByteBuffer.remaining() != 0) {
            SocketChannel sc = this.handler.getSocketChannel();
            if (sc != null && sc.isOpen()) {
                int written = sc.write(this.lastByteBuffer);
                if (written != 0) {
                    if (logger.isLoggable(Level.FINEST)) {
                        logger.finest("Written " + written + " bytes");
                    }
                    continue;
                }
                break;
            }

            throw new IOException("SocketChannel was closed.");
        }

        if (this.lastByteBuffer.remaining() == 0) {
            this.returnBufferBack(this.lastByteBuffer);
            this.lastByteBuffer = null;
        }

    }

    private void returnBufferBack(ByteBuffer byteBuffer) {
        try {
            this.handler.getServer().getByteBufferPool().returnObject(byteBuffer);
        } catch (Exception var3) {
            logger.warning("Error while returning ByteBuffer to pool: " + var3);
        }

    }

    public void forceNotify() {
        if (this.toNotify != null) {
            Object var1 = this.toNotify;
            synchronized(this.toNotify) {
                this.toNotify.notify();
                this.toNotify = null;
            }
        }
    }

    public boolean isDataAvailableForWrite(Object toNotify) {
        if (this.lastByteBuffer != null) {
            if (this.toNotify != null) {
                throw new IllegalStateException("toNotify object was already set!");
            } else {
                this.toNotify = toNotify;
                return true;
            }
        } else if (this.bufferList.size() == 0) {
            return false;
        } else if (this.toNotify != null) {
            throw new IllegalStateException("toNotify object was already set!");
        } else {
            this.toNotify = toNotify;
            return true;
        }
    }

    public void addEncryptedByteBuffer(ByteBuffer buff) {
        this.encryptedBufferList.add(buff);
    }

    public boolean doShutdown() throws IOException {
        if (this.handler.closeIfSSLOutboundDone()) {
            return true;
        } else {
            ByteBuffer dummyByteBuffer = ByteBuffer.allocate(0);
            this.lastByteBuffer = this.handler.encrypt(dummyByteBuffer);
            this.writeLastByteBuffer();
            if (this.lastByteBuffer != null) {
                this.handler.registerWrite();
                return false;
            } else {
                return this.handler.closeIfSSLOutboundDone();
            }
        }
    }
}
